


#ifndef PF_ENTITY_H
#define PF_ENTITY_H

#include <QMultiHash>

#include "pf_math.h"
#include "pf_pen.h"
#include "pf_undoable.h"
#include "pf_vector.h"
#include "lx_Export.h"

class PF_Arc;
class PF_Block;
class PF_Circle;
class PF_Document;
class PF_Container;
class PF_Graphic;
class PF_GraphicView;
class PF_Insert;
class PF_Line;
class PF_Painter;
class PF_Point;
class PF_Polyline;
class PF_Text;
class PF_Layer;



/**
 * Base class for an entity (line, arc, circle, ...)
 *

 */
class LX_EXPORT PF_Entity : public PF_Undoable {
public:


    PF_Entity(PF_Container* parent=NULL);
    virtual ~PF_Entity();

    void init();
    virtual void initId();

    virtual PF_Entity* clone() = 0;

    virtual void reparent(PF_Container* parent) {
        this->parent = parent;
    }

    void resetBorders();

    /**
     * Must be overwritten to return the rtti of this entity
     * (e.g. LX_Define::EntityArc).
     */
    virtual LX_Define::EntityType rtti() const {
        return LX_Define::EntityUnknown;
    }

    /**
     * Identify all entities as undoable entities.
     * @return LX_Define::UndoableEntity
     */
    virtual LX_Define::UndoableType undoRtti() {
        return LX_Define::UndoableEntity;
    }

    /**
     * @return Unique Id of this entity.
     */
    unsigned long int getId() const {
        return id;
    }

	
    /**
     * This method must be overwritten in subclasses and return the 
     * number of <b>atomic</b> entities in this entity.
     */
    virtual unsigned long int count() = 0;
	
    /**
     * This method must be overwritten in subclasses and return the 
     * number of <b>atomic</b> entities in this entity including sub containers.
     */
    virtual unsigned long int countDeep() = 0;


	/**
	 * Implementations must return the total length of the entity
	 * or a negative number if the entity has no length (e.g. a text or hatch).
	 */
	virtual double getLength() {
		return -1.0;
	}

    /**
     * @return Parent of this entity or NULL if this is a root entity.
     */
    PF_Container* getParent() const {
        return parent;
    }

    /**
     * Reparents this entity.
     */
    void setParent(PF_Container* p) {
        parent = p;
    }

    PF_Graphic* getGraphic();
    PF_Block* getBlock();
    PF_Insert* getInsert();
    PF_Entity* getBlockOrInsert();
    PF_Document* getDocument();

    void setLayer(const QString& name);
    void setLayer(PF_Layer* l);
    void setLayerToActive();
    PF_Layer* getLayer(bool resolve = true) const;

    /**
     * Sets the explicit pen for this entity or a pen with special
     * attributes such as BY_LAYER, ..
     */
    void setPen(const PF_Pen& pen) {
        this->pen = pen;
    }
	
	
    void setPenToActive();
    PF_Pen getPen(bool resolve = true) const;

    /**
     * Must be overwritten to return true if an entity type 
     * is a container for other entities (e.g. polyline, group, ...).
     */
    virtual bool isContainer() const = 0;

    /**
     * Must be overwritten to return true if an entity type 
     * is an atomic entity.
     */
    virtual bool isAtomic() const = 0;

    /**
     * Must be overwritten to return true if an entity type 
     * is a potential edge entity of a contour. By default
    * this returns false.
     */
    virtual bool isEdge() const {
        return false;
    }

    /**
     * @return true for all document entities (e.g. Graphics or Blocks).
     * false otherwise.
     */
    virtual bool isDocument() const {
        return false;
    }

    virtual bool setSelected(bool select);
    virtual bool toggleSelected();
    virtual bool isSelected() const;
	virtual bool isParentSelected();
    virtual bool isProcessed() const;
    virtual void setProcessed(bool on);
    virtual bool isInWindow(PF_Vector v1, PF_Vector v2);
    virtual bool hasEndpointsWithinWindow(PF_Vector /*v1*/, PF_Vector /*v2*/) {
        return false;
    }
    virtual bool isVisible();
    virtual void setVisible(bool v) {
        if (v) {
            setFlag(LX_Define::FlagVisible);
        } else {
            delFlag(LX_Define::FlagVisible);
        }
    }
	virtual void setHighlighted(bool on);
	virtual bool isHighlighted();
	
    virtual bool isLocked();

    virtual void undoStateChanged(bool undone);
    virtual bool isUndone() const;

    /**
     * Can be implemented by child classes to update the entities
     * temporary subentities. update() is called if the entity's
     * paramters or undo state changed.
     */
    virtual void update() {}
	
    virtual void setUpdateEnabled(bool on) {
		updateEnabled = on;
	}

    /**
     * This method doesn't do any calculations. 
     * @return minimum coordinate of the entity.
     * @see calculateBorders()
     */
    PF_Vector getMin() const {
        return minV;
    }

    /**
     * This method doesn't do any calculations. 
     * @return minimum coordinate of the entity.
     * @see calculateBorders()
     */
    PF_Vector getMax() const {
        return maxV;
    }

    /**
     * This method returns the difference of max and min returned 
     * by the above functions. 
     * @return size of the entity.
     * @see calculateBorders()
     * @see getMin()
     * @see getMax()
     */
    PF_Vector getSize() const {
        return maxV-minV;
    }

    void addGraphicVariable(const QString& key, double val, int code);
    void addGraphicVariable(const QString& key, int val, int code);
    void addGraphicVariable(const QString& key, const QString& val, int code);
	
    double getGraphicVariableDouble(const QString& key, double def);
    int getGraphicVariableInt(const QString& key, int def);
    QString getGraphicVariableString(const QString& key,
                                       const QString& def);

	LX_Define::Unit getGraphicUnit();

    /**
     * Must be overwritten to get all reference points of the entity. 
     */
    virtual PF_VectorSolutions getRefPoints() {
		PF_VectorSolutions ret;
		return ret;
	}


    /**
     * Must be overwritten to get the closest endpoint to the 
     * given coordinate for this entity. 
     *
     * @param coord Coordinate (typically a mouse coordinate)
     * @param dist Pointer to a value which will contain the measured
     * distance between 'coord' and the closest endpoint. The passed
     * pointer can also be NULL in which case the distance will be
     * lost.
     *
     * @return The closest endpoint.
     */
    virtual PF_Vector getNearestEndpoint(const PF_Vector& coord,
                                         double* dist = NULL) = 0;

    /**
     * Must be overwritten to get the closest coordinate to the 
    * given coordinate which is on this entity.
     *
     * @param coord Coordinate (typically a mouse coordinate)
     * @param dist Pointer to a value which will contain the measured
     * distance between \p coord and the point. The passed pointer can 
     * also be \p NULL in which case the distance will be lost.
     *
     * @return The closest coordinate.
     */
    virtual PF_Vector getNearestPointOnEntity(const PF_Vector& /*coord*/,
            bool onEntity = true, double* dist = NULL,
            PF_Entity** entity = NULL) = 0;

    /**
     * Must be overwritten to get the (nearest) center point to the 
     * given coordinate for this entity. 
     *
     * @param coord Coordinate (typically a mouse coordinate)
     * @param dist Pointer to a value which will contain the measured
     * distance between 'coord' and the closest center point. The passed
     * pointer can also be NULL in which case the distance will be
     * lost.
     *
     * @return The closest center point.
     */
    virtual PF_Vector getNearestCenter(const PF_Vector& coord,
                                       double* dist = NULL) = 0;

    /**
     * Must be overwritten to get the (nearest) middle point to the 
     * given coordinate for this entity. 
     *
     * @param coord Coordinate (typically a mouse coordinate)
     * @param dist Pointer to a value which will contain the measured
     * distance between 'coord' and the closest middle point. The passed
     * pointer can also be NULL in which case the distance will be
     * lost.
     *
     * @return The closest middle point.
     */
    virtual PF_Vector getNearestMiddle(const PF_Vector& coord,
                                       double* dist = NULL) = 0;

    /**
     * Must be overwritten to get the nearest point with a given
     * distance to the endpoint to the given coordinate for this entity. 
     *
     * @param distance Distance to endpoint.
     * @param coord Coordinate (typically a mouse coordinate)
     * @param dist Pointer to a value which will contain the measured
     * distance between 'coord' and the closest point. The passed
     * pointer can also be NULL in which case the distance will be
     * lost.
     *
     * @return The closest point with the given distance to the endpoint.
     */
    virtual PF_Vector getNearestDist(double distance,
                                     const PF_Vector& coord,
                                     double* dist = NULL) = 0;
									 
    /**
     * Must be overwritten to get the point with a given
     * distance to the start- or endpoint to the given coordinate for this entity. 
     *
     * @param distance Distance to endpoint.
     * @param startp true = measured from Startpoint, false = measured from Endpoint
     *
     * @return The point with the given distance to the start- or endpoint.
     */
    virtual PF_Vector getNearestDist(double /*distance*/,
                                     bool /*startp*/) {
		return PF_Vector(false);
	}
									 
    /**
     * Must be overwritten to get the nearest reference point for this entity. 
     *
     * @param coord Coordinate (typically a mouse coordinate)
     * @param dist Pointer to a value which will contain the measured
     * distance between 'coord' and the closest point. The passed
     * pointer can also be NULL in which case the distance will be
     * lost.
     *
     * @return The closest point with the given distance to the endpoint.
     */
    virtual PF_Vector getNearestRef(const PF_Vector& coord,
                                     double* dist = NULL) { 
		PF_VectorSolutions s = getRefPoints();
		
		return s.getClosest(coord, dist); 
	}
	
    /**
     * Gets the nearest reference point of this entity if it is selected. 
	 * Containers re-implement this method to return the nearest reference
	 * point of a selected sub entity.
     *
     * @param coord Coordinate (typically a mouse coordinate)
     * @param dist Pointer to a value which will contain the measured
     * distance between 'coord' and the closest point. The passed
     * pointer can also be NULL in which case the distance will be
     * lost.
     *
     * @return The closest point with the given distance to the endpoint.
     */
    virtual PF_Vector getNearestSelectedRef(const PF_Vector& coord,
                                     double* dist = NULL) { 
		if (isSelected()) {
			return getNearestRef(coord, dist);
		}
		else {
			return PF_Vector(false); 
		}
	}

    /**
     * Must be overwritten to get the shortest distance between this 
     * entity and a coordinate.
     *
     * @param coord Coordinate (typically a mouse coordinate)
     * @param entity Pointer which will contain the (sub-)entity which is 
     *               closest to the given point or NULL if the caller is not 
     *               interested in this information.
     * @param level The resolve level. 
     *
     * @sa LX_Define::ResolveLevel
     *
     * @return The measured distance between \p coord and the entity.
     */
    virtual double getDistanceToPoint(const PF_Vector& coord,
                                      PF_Entity** entity = NULL,
                                      LX_Define::ResolveLevel level = LX_Define::ResolveNone,
									  double solidDist = LX_DOUBLEMAX) = 0;

    virtual bool isPointOnEntity(const PF_Vector& coord,
                                 double tolerance=PF_TOLERANCE);

    /**
     * Implementations must move the entity by the given vector.
     */
    virtual void move(PF_Vector offset) = 0;

    /**
     * Implementations must rotate the entity by the given angle around
     * the given center.
     */
    virtual void rotate(PF_Vector center, double angle) = 0;

    /**
     * Implementations must scale the entity by the given factors.
     */
    virtual void scale(PF_Vector center, PF_Vector factor) = 0;

    /**
     * Acts like scale(PF_Vector) but with equal factors.
     * Equal to scale(center, PF_Vector(factor, factor)).
     */
    virtual void scale(PF_Vector center, double factor) {
        scale(center, PF_Vector(factor, factor));
    }

    /**
     * Implementations must mirror the entity by the given axis.
     */
    virtual void mirror(PF_Vector axisPoint1, PF_Vector axisPoint2) = 0;

    virtual void stretch(PF_Vector firstCorner,
                         PF_Vector secondCorner,
                         PF_Vector offset);

	/**
	 * Implementations must drag the reference point(s) of all
	 * (sub-)entities that are very close to ref by offset.
	 */
	virtual void moveRef(const PF_Vector& /*ref*/, 
		const PF_Vector& /*offset*/) {
		return;
	}
	
	/**
	 * Implementations must drag the reference point(s) of selected 
	 * (sub-)entities that are very close to ref by offset.
	 */
	virtual void moveSelectedRef(const PF_Vector& /*ref*/, 
		const PF_Vector& /*offset*/) {
		return;
	}

    /**
     * Implementations must draw the entity on the given device.
     */
    virtual void draw(PF_Painter* painter, PF_GraphicView* view, 
		double patternOffset = 0.0) = 0;

	double getStyleFactor(PF_GraphicView* view);
	
        QString getUserDefVar(QString key);
        QList<QString> getAllKeys();
        void setUserDefVar(QString key, QString val);
        void delUserDefVar(QString key);

    friend std::ostream& operator << (std::ostream& os, PF_Entity& e);

    /** Recalculates the borders of this entity. */
    virtual void calculateBorders() = 0;

protected:
    //! Entity's parent entity or NULL is this entity has no parent.
    PF_Container* parent;
    //! minimum coordinates
    PF_Vector minV;
    //! maximum coordinates
    PF_Vector maxV;

    //! Pointer to layer
    PF_Layer* layer;

    //! Entity id
    unsigned long int id;

    //! pen (attributes) for this entity
    PF_Pen pen;
	
	//! auto updating enabled?
	bool updateEnabled;

private:
        QMultiHash<QString, QString> varList;
};

#endif
